# -*- coding: utf-8 -*-
"""
用户管理服务

提供用户相关的数据库操作：
- 用户认证
- 用户信息查询  
- 密码管理
- 登录状态管理
"""

from typing import Optional, Dict, Any
from datetime import datetime, timedelta
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, and_, or_, update
import logging

from ..models.rbac import User, UserStatus
from ..auth.jwt_auth import password_manager
from ..core.exceptions import AuthenticationError, ValidationError

logger = logging.getLogger(__name__)


class UserService:
    """用户服务类"""
    
    def __init__(self, db: AsyncSession):
        self.db = db
    
    async def authenticate_user(self, username: str, password: str) -> Optional[User]:
        """
        验证用户凭据
        
        :param username: 用户名或邮箱
        :param password: 明文密码
        :return: 验证成功返回User对象，失败返回None
        :raises AuthenticationError: 用户被锁定或状态异常时抛出
        """
        # 查询用户（支持用户名或邮箱登录）
        query = select(User).where(
            and_(
                or_(
                    User.username == username,
                    User.email == username
                ),
                User.status != UserStatus.DELETED
            )
        )
        
        result = await self.db.execute(query)
        user = result.scalar_one_or_none()
        
        if not user:
            return None
        
        # 检查用户状态
        if user.status == UserStatus.LOCKED:
            # 检查锁定是否已过期
            if user.locked_until and user.locked_until > datetime.now():
                raise AuthenticationError(f"账户已被锁定，解锁时间：{user.locked_until.strftime('%Y-%m-%d %H:%M:%S')}")
            elif user.locked_until and user.locked_until <= datetime.now():
                # 锁定已过期，自动解锁
                await self.unlock_user(user.id)
                user.status = UserStatus.ACTIVE
                user.locked_until = None
                user.login_attempts = 0
        
        if user.status == UserStatus.SUSPENDED:
            raise AuthenticationError("账户已被停用，请联系管理员")
        
        if user.status != UserStatus.ACTIVE:
            raise AuthenticationError("账户状态异常，无法登录")
        
        # 验证密码
        if not password_manager.verify_password(password, user.password_hash):
            # 密码错误，增加失败次数
            await self.increment_login_attempts(user.id)
            return None
        
        # 密码验证成功，重置登录尝试次数并更新登录信息
        await self.reset_login_attempts(user.id)
        await self.update_login_info(user.id)
        
        return user
    
    async def get_user_by_id(self, user_id: int) -> Optional[User]:
        """根据ID获取用户"""
        query = select(User).where(
            and_(
                User.id == user_id,
                User.status != UserStatus.DELETED
            )
        )
        
        result = await self.db.execute(query)
        return result.scalar_one_or_none()
    
    async def get_user_by_username(self, username: str) -> Optional[User]:
        """根据用户名获取用户"""
        query = select(User).where(
            and_(
                User.username == username,
                User.status != UserStatus.DELETED
            )
        )
        
        result = await self.db.execute(query)
        return result.scalar_one_or_none()
    
    async def increment_login_attempts(self, user_id: int) -> None:
        """增加登录失败次数"""
        user = await self.get_user_by_id(user_id)
        if not user:
            return
        
        user.login_attempts = (user.login_attempts or 0) + 1
        
        # 如果失败次数达到限制，锁定账户
        max_attempts = 5  # 最大尝试次数
        if user.login_attempts >= max_attempts:
            user.status = UserStatus.LOCKED
            user.locked_until = datetime.now() + timedelta(minutes=30)  # 锁定30分钟
            logger.warning(f"用户 {user.username} 因登录失败次数过多被锁定")
        
        # 添加到会话并提交
        self.db.add(user)
        await self.db.commit()
    
    async def reset_login_attempts(self, user_id: int) -> None:
        """重置登录失败次数"""
        user = await self.get_user_by_id(user_id)
        if not user:
            return
        
        user.login_attempts = 0
        user.locked_until = None
        
        if user.status == UserStatus.LOCKED and user.locked_until:
            # 如果是因为登录失败被锁定，现在可以解锁
            user.status = UserStatus.ACTIVE
        
        self.db.add(user)
        await self.db.commit()
    
    async def update_login_info(self, user_id: int, ip_address: str = None) -> None:
        """更新用户登录信息"""
        user = await self.get_user_by_id(user_id)
        if not user:
            return
        
        user.last_login_at = datetime.now()
        user.login_count = (user.login_count or 0) + 1
        
        if ip_address:
            user.last_login_ip = ip_address
        
        self.db.add(user)
        await self.db.commit()
    
    async def unlock_user(self, user_id: int) -> None:
        """解锁用户"""
        user = await self.get_user_by_id(user_id)
        if not user:
            return
        
        if user.status == UserStatus.LOCKED:
            user.status = UserStatus.ACTIVE
            user.locked_until = None
            user.login_attempts = 0
            
            self.db.add(user)
            await self.db.commit()
            logger.info(f"用户 {user.username} 已解锁")
    
    async def change_password(self, user_id: int, current_password: str, new_password: str) -> None:
        """
        修改用户密码
        
        :param user_id: 用户ID
        :param current_password: 当前密码
        :param new_password: 新密码
        :raises AuthenticationError: 当前密码错误
        :raises ValidationError: 新密码不符合要求
        """
        user = await self.get_user_by_id(user_id)
        if not user:
            raise AuthenticationError("用户不存在")
        
        # 验证当前密码
        if not password_manager.verify_password(current_password, user.password_hash):
            raise AuthenticationError("当前密码错误")
        
        # 验证新密码强度
        if not password_manager.validate_password_strength(new_password):
            raise ValidationError("密码强度不足，密码至少8位且包含字母和数字")
        
        # 更新密码
        user.password_hash = password_manager.hash_password(new_password)
        user.password_updated_at = datetime.now()
        
        self.db.add(user)
        await self.db.commit()
        logger.info(f"用户 {user.username} 密码已更新")
    
    async def get_user_info(self, user_id: int) -> Optional[Dict[str, Any]]:
        """获取用户信息（用于API返回）"""
        user = await self.get_user_by_id(user_id)
        if not user:
            return None
        
        # 获取用户权限（这里简化处理，实际应该从RBAC服务获取）
        permissions = []
        if user.is_superuser:
            permissions = ["admin", "customer_read", "customer_write", "customer_delete"]
        else:
            permissions = ["customer_read"]  # 默认权限
        
        return {
            "user_id": user.id,
            "username": user.username,
            "real_name": user.real_name,
            "nickname": user.nickname,
            "email": user.email,
            "phone": user.phone,
            "department": user.department,
            "position": user.position,
            "avatar_url": user.avatar_url,
            "status": user.status.value,
            "is_superuser": user.is_superuser,
            "last_login_at": user.last_login_at.isoformat() if user.last_login_at else None,
            "permissions": permissions
        }


def get_user_service(db: AsyncSession) -> UserService:
    """获取用户服务实例"""
    return UserService(db)